/*
TRK300
- OPM ADC采用对数放大器
- LS 没用ADC，仅DAC控制

2021.7.28 by Menglong Wu
*/
#include <assert.h>
#include "port.h"
#include "fpga-common.h"
#include "fpga-private.h"
#include "interface/protocal-otdr.h"
#include <stdio.h>
// #include <math.h>
#include <sys/ioctl.h>

#include "bsp-trk300.h"
// #define SET_LS 0x41
#define SET_LS 0x16

// #define IO_DAC (0)
// #define IO_DBM (10)
// #define IO_LS_MODE (11)


struct line_kc
{
    float k;
    float c;
};

float dbm_dac(float dbm, struct line_kc *cal);
float adc_dbm(int adc, struct line_kc *cal);


static struct line_kc lpd, lpa;  // 两条线性 1310

static struct line_kc opma;       // 光功1310
static struct line_kc opma_1550;  // 光功1550

// struct trk300_ls
// {
// 	short dac;
// 	short mode;

// #define LS_MODE_CW 0
// #define LS_MODE_OFF 1
// #define LS_MODE_270HZ 2
// #define LS_MODE_1KHZ 3
// #define LS_MODE_2KHZ 4

// } __attribute__((__packed__));


static short mode = LS_MODE_CW;
static int   set_gpio(struct fpga_dev *dev, int io, int val)
{
    assert(dev->desc == dev_desc);

    struct trk300_ls tbuf;
    char             rbuf[MIN_RX_PACK_SIZE];

    if (io == IO_MODE) {
	mode = val;
	return 0;
    }
    tbuf.mode = mode;

    if (io == IO_DAC) {
	tbuf.dac = (val & 0xff) << 8 |
	           (val & 0xff00) >> 8;
	tbuf.dac = val;

	int ret = fpga_send_data(dev,
	                         SET_LS,
	                         &tbuf,
	                         sizeof(struct trk300_ls),
	                         rbuf,
	                         sizeof(rbuf));
    }
    else if (io == IO_DBM) {
	float f_dac, f_adc, f_val;
	int   dac, adc;
	f_val = (float)val / 1000;
	f_dac = dbm_dac((float)f_val, &lpd);
	f_adc = dbm_dac((float)f_val, &lpa);
	dac   = (int)f_dac;
	adc   = (int)f_adc;
	set_gpio(dev, IO_DAC, dac);
    }
    return 0;
}


/*
**	10:CH0     	11:CH1    	16:CH6  	 17:Vbat(内部除4)
**      20:CH0(开启除2) 21:CH1(开启除2) 26::CH6(开启除2)
*/

#define MXS_ADC_CH0       10
#define MXS_ADC_CH1       11
#define MXS_ADC_CH6       16
#define MXS_ADC_BAT       17

#define MXS_ADC_CH0_DIV_2 20
#define MXS_ADC_CH1_DIV_2 21
#define MXS_ADC_CH6_DIV_2 26

int get_bat_vol(int *adc)
{
    int  iRes;
    int  fd;
    int  tmp1 = 0, tmp2 = 0;
    char device[] = "/dev/mxs-adc";

    fd = open(device, O_RDONLY);
    if (fd < 0) {
	printf("%s() %d:fail open device %s\n", __FUNCTION__, __LINE__, device);
	perror("");
	return -1;
    }

#if 0
	ioctl(fd, 10, &iRes);
	printf("10:%d\t", iRes);

	ioctl(fd, 11, &iRes);
	printf("11:%d\t", iRes);

	ioctl(fd, 16, &iRes);
	printf("16:%d\t", iRes);

	ioctl(fd, 17, &iRes);
	printf("17:%d\t", iRes);

	ioctl(fd, 20, &iRes);
	printf("20:%d\t", iRes);

	ioctl(fd, 21, &iRes);
	printf("21:%d\t", iRes);

	ioctl(fd, 26, &iRes);
	printf("26:%d\t\r\n", iRes);
#endif
    for (int i = 0; i < 16; i++) {
	ioctl(fd, MXS_ADC_CH1, &iRes);
	tmp1 += iRes;
    }
    tmp1 = tmp1 >> 4;
    *adc = tmp1;

    close(fd);
    return 0;
}

static int get_gpio_hsadc(struct fpga_dev *dev, int io, int *val)
{
    int            fd = open("/dev/mxs-hsadc0", O_RDWR);
    unsigned short rbuf[32];
    int            unuse_len;

    unuse_len = read(fd, rbuf, sizeof(rbuf) / sizeof(unsigned short));
    close(fd);

    if (io == IO_DAC) {
	*val = rbuf[0];
    }
    else if (io == IO_DBM) {
	float f_dbm;
	int   tmp = rbuf[0];

	f_dbm = adc_dbm(tmp, &opma);
	*val  = (int)(f_dbm * 100);
    }


    // printf("v1 %d v2 %d\r\n", v1, v2);
    return 0;
}


static int get_gpio_adc(struct fpga_dev *dev, int io, int *val)
{
    assert(dev->desc == dev_desc);


    if (io == IO_DAC) {
	int v1;
	get_bat_vol(&v1);
	*val = v1;
    }
    else if (io == IO_DAC_1310) {
	float f_dbm;
	int   tmp;
	int   v1;
	get_bat_vol(&v1);
	tmp = v1;

	f_dbm = adc_dbm(tmp, &opma);
	// printf("dbm f %f adc %d\r\n", f_dbm, tmp);
	*val = (int)(f_dbm * 100);
    }
    else if (io == IO_DAC_1550) {
	float f_dbm;
	int   tmp;
	int   v1;
	get_bat_vol(&v1);
	tmp = v1;

	f_dbm = adc_dbm(tmp, &opma_1550);
	// printf("%f \r\n", f_dbm);
	*val = (int)(f_dbm * 100);
    }
    return 0;
}


float      env_pt1_dbm = -10.45;
static int env_pt1_dac = 300;
static int env_pt1_adc = 260;


float      env_pt2_dbm = -8.75;
static int env_pt2_dac = 400;
static int env_pt2_adc = 417;

float      env_opm_pt1_dbm = -10.45;
static int env_opm_pt1_adc = 260;


float      env_opm_pt2_dbm = -8.75;
static int env_opm_pt2_adc = 417;


float      env_opm_pt1_dbm_1550 = -10.45;
static int env_opm_pt1_adc_1550 = 260;


float      env_opm_pt2_dbm_1550 = -8.75;
static int env_opm_pt2_adc_1550 = 417;


struct line_kc calc_line(float x1, float y1, float x2, float y2)
{
    struct line_kc cal;
    cal.k = (float)(y2 - y1) / (x2 - x1);
    cal.c = y2 - cal.k * x2;
    // printf("k %f c %f\r\n", cal.k, cal.c);
    return cal;
}

float dbm_dac(float dbm, struct line_kc *cal)
{
    float y, x = pow(10, dbm / 10);
    y = cal->k * x + cal->c;
    // printf("dbm-dac %f %f\r\n", x, y);
    return y;
}

#define LOG_APM (1)  // 对数放大器
float adc_dbm(int adc, struct line_kc *cal)
{
#if LOG_APM
    // 因为采用对数放大器，所以这里的计算可以线性
    float y, x = adc;
    y = cal->k * x + cal->c;
    return y;
#else
    /*
      这个线性k=0.01，c=5，导致最后log(y)里面的y是负数，计算无效。
      不该这么算？
      就是因为使用对数放大器，所以这种线性转对数的方法会出现负数，最终计算无效
    */
    float y, x = pow(10, adc / 10);
    y = cal->k * x + cal->c;
    // printf("y = %f %f\r\n", y, 10 * log(y) / log(10));
    return 10 * log(y) / log(10);
#endif
}


void init_line()
{
    lpd = calc_line(
	    pow(10, env_pt1_dbm / 10),
	    env_pt1_dac,
	    pow(10, env_pt2_dbm / 10),
	    env_pt2_dac);
    lpa = calc_line(
	    pow(10, env_pt1_dbm / 10),
	    env_pt1_adc,
	    pow(10, env_pt2_dbm / 10),
	    env_pt2_adc);

#if LOG_APM
    opma = calc_line(
	    env_opm_pt1_adc,
	    env_opm_pt1_dbm,
	    env_opm_pt2_adc,
	    env_opm_pt2_dbm);
#else
    opma = calc_line(
	    env_opm_pt1_adc,
	    pow(10, env_opm_pt1_dbm / 10),
	    env_opm_pt2_adc,
	    pow(10, env_opm_pt2_dbm / 10));
#endif

#if LOG_APM
    opma_1550 = calc_line(
	    env_opm_pt1_adc_1550,
	    env_opm_pt1_dbm_1550,
	    env_opm_pt2_adc_1550,
	    env_opm_pt2_dbm_1550);
#else
    opma_1550 = calc_line(
	    env_opm_pt1_adc_1550,
	    pow(10, env_opm_pt1_dbm_1550 / 10),
	    env_opm_pt2_adc_1550,
	    pow(10, env_opm_pt2_dbm_1550 / 10));
#endif
}

static void __attribute__((constructor(INIT_ENV))) _load_env()
{
    char *env = NULL;

    env = getenv("PT1_DBM");
    if (env) {
	env_pt1_dbm = atof(env);
    }
    env = getenv("PT1_DAC");
    if (env) {
	env_pt1_dac = atoi(env);
    }
    env = getenv("PT1_ADC");
    if (env) {
	env_pt1_adc = atoi(env);
    }

    env = getenv("PT2_DBM");
    if (env) {
	env_pt2_dbm = atof(env);
    }
    env = getenv("PT2_DAC");
    if (env) {
	env_pt2_dac = atoi(env);
    }
    env = getenv("PT2_ADC");
    if (env) {
	env_pt2_adc = atoi(env);
    }


    env = getenv("OPM_PT1_DBM");
    if (env) {
	env_opm_pt1_dbm = atof(env);
    }
    env = getenv("OPM_PT1_ADC");
    if (env) {
	env_opm_pt1_adc = atoi(env);
    }

    env = getenv("OPM_PT2_DBM");
    if (env) {
	env_opm_pt2_dbm = atof(env);
    }
    env = getenv("OPM_PT2_ADC");
    if (env) {
	env_opm_pt2_adc = atoi(env);
    }

    env = getenv("OPM_PT1_DBM_1550");
    if (env) {
	env_opm_pt1_dbm_1550 = atof(env);
    }
    env = getenv("OPM_PT1_ADC_1550");
    if (env) {
	env_opm_pt1_adc_1550 = atoi(env);
    }

    env = getenv("OPM_PT2_DBM_1550");
    if (env) {
	env_opm_pt2_dbm_1550 = atof(env);
    }
    env = getenv("OPM_PT2_ADC_1550");
    if (env) {
	env_opm_pt2_adc_1550 = atoi(env);
    }

    // printf("---|%f %d %d\r\n", env_pt1_dbm, env_pt1_dac, env_pt1_adc);
    // printf("---|%f %d %d\r\n", env_pt2_dbm, env_pt2_dac, env_pt2_adc);
    // printf("---|%f %d\r\n", env_opm_pt1_dbm, env_opm_pt1_adc);
    // printf("---|%f %d\r\n", env_opm_pt2_dbm, env_opm_pt2_adc);

    init_line();
}

void _load_env_ls_opm()
{
    // TODO 载入环境变量
    _load_env();
}


void ls_workthread(void *ptr)
{
}


static struct priv_otdr_data data_32k = {
    .cache_len   = 32000 * 4 + 4000,
    .sample_byte = 32 * 1000 * 4,
};


static struct fpga_if otdr_interface_32k = {
    DEFAULT_FPGA_IF,
    .pfun_probe   = cb_fpga_otdr_probe,
    .pfun_release = cb_fpga_otdr_release,
    .private_data = &data_32k,
    .name         = "this-otdr-lsopm-32k",
    // .pfun_workthread = cb_workthread,
    .pfun_start      = cb_fpga_otdr_start,
    .pfun_isfinish   = cb_fpga_otdr_isfinish,
    .pfun_stop       = cb_fpga_otdr_stop,
    .pfun_set_gpio   = set_gpio,
    .pfun_get_gpio   = get_gpio_adc,
    .pfun_version    = com_fpga_version,
    .pfun_read_frame = cb_fpga_read_otdr_data,
    .pfun_write_regs = cb_fpga_otdr_write_regs,
};

static struct priv_otdr_data data_64k = {
    .cache_len   = 64000 * 4 + 4000,
    .sample_byte = 64 * 1000 * 4,
};

static struct fpga_if otdr_interface_64k = {
    DEFAULT_FPGA_IF,
    .pfun_probe      = cb_fpga_otdr_probe,
    .private_data    = &data_64k,
    .name            = "this-otdr-lsopm-64k",
    .pfun_start      = cb_fpga_otdr_start,
    .pfun_isfinish   = cb_fpga_otdr_isfinish,
    .pfun_stop       = cb_fpga_otdr_stop,
    .pfun_set_gpio   = set_gpio,
    .pfun_get_gpio   = get_gpio_adc,
    .pfun_version    = com_fpga_version,
    .pfun_read_frame = cb_fpga_read_otdr_data,
    .pfun_write_regs = cb_fpga_otdr_write_regs,
};

static void __attribute__((constructor(INIT_BSP))) this_init()
{
    fpga_if_register(&otdr_interface_32k);
    fpga_if_register(&otdr_interface_64k);
}